<?php
/**
 * Here is your custom functions.
 */

use teamones\Request;

if (!function_exists('throw_strack_exception')) {

    /**
     * 抛出Strack异常
     * @param $msg
     * @param int $code
     * @param array $data
     * @return mixed
     * @throws \app\exception\HttpResponseException
     */
    function throw_strack_exception($msg, int $code = -400000, array $data = [])
    {
        $errorData = [
            "code" => $code,
            "msg" => $msg,
            "data" => $data
        ];

        throw new \app\exception\HttpResponseException(json($errorData));
    }
}


if (!function_exists('teamones_request')) {

    /**
     * @param string $serviceName
     * @param string $route
     * @param $data
     * @param $headers
     * @param bool $autoXu
     * @param string $method
     * @return array|mixed|\Yurun\Util\YurunHttp\Http\Response
     */
    function teamones_request(string $serviceName, string $route, $data, $headers = [], bool $autoXu = true, string $method = 'POST')
    {
        $r = Request::connection();
        if ($autoXu) {
            $headers['X-Userinfo'] = request()->getXUserInfo();
        }
        $r->setMethod($method)
            ->setHeader($headers)
            ->setServerHost($serviceName)
            ->setRoute($route);
        if (isset($data)) {
            $r->setBody($data);
        }
        return $r->request();
    }
}


if (!function_exists('array_sort_by')) {
    /**
     * 数组排序
     * @param $arr
     * @param $key
     * @param string $type
     * @param bool $showKey
     * @return array
     */
    function array_sort_by($arr, $key, $type = 'asc', $showKey = true)
    {
        $keyValue = [];
        foreach ($arr as $k => $value) {
            $keyValue[$k] = $value[$key];
        }
        if (strtolower($type) === 'asc') {
            asort($keyValue);
        } else {
            arsort($keyValue);
        }
        reset($keyValue);

        $sortData = [];
        foreach ($keyValue as $k => $value) {
            if ($showKey) {
                $sortData[$k] = $arr[$k];
            } else {
                $sortData[] = $arr[$k];
            }
        }
        return $sortData;
    }
}

if (!function_exists('get_month_time')) {
    /**
     * 获取指定日期所在月的开始日期与结束日期(时间戳)
     * @param $date
     * @return array
     */
    function get_month_time($date): array
    {
        $ret = array();
        $timestamp = strtotime($date);
        $mdays = date('t', $timestamp);
        $ret['sdate'] = strtotime(date('Y-m-1 00:00:00', $timestamp));
        $ret['edate'] = strtotime(date('Y-m-' . $mdays . ' 23:59:59', $timestamp));
        return $ret;
    }
}

if (!function_exists('get_day_range_unix')) {
    /**
     * 获取指定日期的当天unix时间戳范围
     *
     * 默认为今天
     * @param int $dateUnix
     * @return array
     */
    function get_day_range_unix(int $dateUnix = 0): array
    {
        $dateUnix = empty($dateUnix) ? time() : $dateUnix;
        $startTime = strtotime(date('Y-m-d', $dateUnix));
        $endTime = strtotime('+1 day', $startTime);
        return [
            'sdate' => $startTime,
            'edate' => $endTime,

        ];
    }
}

if (!function_exists('array_group_by')) {
    /**
     * 对二维数组进行分组
     * @param $arr
     * @param $key
     * @return array
     */
    function array_group_by($arr, $key): array
    {
        $grouped = [];
        foreach ($arr as $value) {
            $grouped[$value[$key]][] = $value;
        }

        if (func_num_args() > 2) {
            $args = func_get_args();
            foreach ($grouped as $groupKey => $value) {
                $param = array_merge([$value], array_slice($args, 2, func_num_args()));
                $grouped[$groupKey] = call_user_func_array('array_group_by', $param);
            }
        }
        return $grouped;
    }
}

if (!function_exists('param_to_array')) {
    /**
     * 将`int/string/array`统一成数组，并对元素执行回调
     * @param $paramVal
     * @param string $separator
     * @param callable|null $callback
     * @return array
     */
    function param_to_array($paramVal, string $separator = ',', callable $callback = null): array
    {
        $res = split_array($paramVal, $separator);
        if (is_callable($callback)) {
            return array_map($callback, $res);
        }
        return $res;
    }
}

if (!function_exists('filter_extract')) {
    /**
     * 从通用filter中获取参数，丢弃条件
     * @param $filterObj
     * @param string $separator
     * @param bool $returnArray
     * @param callable|null $callback
     * @return mixed
     */
    function filter_extract($filterObj, string $separator = ',', bool $returnArray = true, callable $callback = null)
    {
        if (empty($filterObj)) {
            return $filterObj;
        }
        $validOp = ['-in'];
        $op = is_array($filterObj) ? $filterObj[array_key_first($filterObj)] : 'no operation for this filter object';
        if (in_array($op, $validOp)) {
            $res = array_slice($filterObj, 1);
        } else {
            $res = $filterObj;
            if (is_string($op) && strpos($op, '-') === 0) {
                return $filterObj;
            }
        }
        if (!is_array($res)) {
            $res = explode($separator, $res); // 分隔符？
        } else {
            $tempRes = [];
            foreach ($res as $item) {
                if (is_array($item)) {
                    return $filterObj; // TODO： 多维数组处理?
                }
                array_push($tempRes, ...explode($separator, $item));
            }
            $res = $tempRes;
        }
        $res = $returnArray ? $res : implode($separator, $res);
        if (is_callable($callback)) {
            $res = $returnArray ? array_map($callback, $res) : $callback($res);
        }
        return $res;
    }
}

if (!function_exists('split_array')) {
    /**
     * 将`int/string/array`统一成数组
     * @param mixed $paramVal
     * @param string $separator
     * @return array
     */
    function split_array($paramVal = [], string $separator = ','): array
    {
        if (!is_array($paramVal)) {
            if (empty($paramVal)) {
                return [];
            } else if (is_string($paramVal)) {
                return explode($separator, $paramVal);
            } else {
                return [$paramVal];
            }
        }
        return $paramVal;
    }
}

if (!function_exists('query_array_construct')) {
    /**
     * 构造查询数组
     * @param array $structure 构造数组结构
     * @param array $originArray 数据源
     * @return array
     */
    function query_array_construct(array $structure, array $originArray): array
    {
        $structure['_final'] = 'array_shift';
        $originArray = [$originArray];
        return callback_format_array($structure, $originArray);
    }
}

if (!function_exists('result_array_construct')) {
    /**
     * 构造结果数组
     * @param array $structure 构造数组结构
     * @param array $originArray 数据源
     * @return array
     */
    function result_array_construct(array $structure, array $originArray): array
    {
        return callback_format_array($structure, $originArray);
    }
}

if (!function_exists('callback_format_array')) {
    /**
     * 通过结构构造数组
     *
     * 结构内为 `key => callback` 键值对;
     * `callback`支持传入闭包,
     * 支持`callbackA,param1, ...paramN|callbackB`,
     * 支持`\`（如`explode,\,`）,
     * 数组键值为`key`的数据将被作为回调的最后一个参数;
     * `key`为`final`时支持对结果执行回调
     * @param array $structure 构造数组结构
     * @param array $originArray 数据源
     * @return array
     */
    function callback_format_array(array $structure, array $originArray): array
    {
        $res = [];
        if (empty($structure)) {
            return $originArray;
        }
        $finalCall = null;
        if (isset($structure['_final'])) {
            $finalCall = $structure['_final'];
            unset($structure['_final']);
        }
        // 回调方法解析
        $callbackMap = [];
        foreach ($structure as $key => $callbackList) {
            if (is_callable($callbackList)) {
                $callbackList = [$callbackList];
            } else {
                $callbackList = explode_special($callbackList, '|');
            }
            foreach ($callbackList as $callbackRow) {
                if (is_callable($callbackRow)) {
                    $callback = $callbackRow;
                    $callbackRow = [];
                } else {
                    $callbackRow = explode_special($callbackRow, ',');
                    $callback = array_shift($callbackRow);
                }
                // 如果传入非回调，也写入，避免错误被跳过
                if (!isset($callbackMap[$key])) {
                    $callbackMap[$key] = [];
                }
                $callbackMap[$key][] = [
                    'callback' => $callback,
                    'parameters' => $callbackRow
                ];
            }
        }
        foreach ($originArray as $itemKey => $item) {
            $temp = [];
            foreach ($callbackMap as $key => $callbackList) {
                if (!isset($temp[$key])) {
                    $temp[$key] = $item[$key] ?? null;
                }
                foreach ($callbackList as $callbackItem) {
                    // 插入变量值
                    $valueIndex = array_search('{{' . $key . '}}', $callbackItem['parameters']);
                    if ($valueIndex !== false) {
                        $callbackItem['parameters'] = array_replace($callbackItem['parameters'], [$valueIndex => $temp[$key]]);
                    } else {
                        $callbackItem['parameters'][] = $temp[$key];
                    }
                    $temp[$key] = empty($callbackItem['callback']) ? $temp[$key] : $callbackItem['callback'](...$callbackItem['parameters']);
                }
            }
            $res[$itemKey] = $temp;
        }
        if (!empty($finalCall) && is_callable($finalCall)) {
            return $finalCall($res);
        }
        return $res;
    }
}

if (!function_exists('explode_special')) {
    /**
     * 带转义符的分割
     * @param string $data
     * @param string $separator
     * @param bool $keepSlash 保留分隔符转义
     * @return array
     */
    function explode_special(string $data, string $separator = ',', bool $keepSlash = false): array
    {
        $tempRow = explode($separator, $data);
        $res = [];
        while (!empty($tempRow)) {
            $item = array_shift($tempRow);
            $itemTemp = str_replace('\\\\', $separator, $item);
            if (strrpos($itemTemp, '\\') === strlen($itemTemp) - 1) {
                $item = substr($item, 0, strlen($item) - 1);
                $item .= ($keepSlash ? '\\' : '') . $separator;
                if (!empty($tempRow)) {
                    $item .= array_shift($tempRow);
                }
            }
            $res[] = $item;
        }
        return $res;
    }
}

if (!function_exists('get_all_month')) {
    /**
     * 获得某个时间段内，所有的自然月
     * @param array $data
     * @return array
     */
    function get_all_month(array $data): array
    {
        $firstDate = $data[0];
        $lastDate = $data[1];
        $start = intval(date("m", strtotime($firstDate)));
        $end = intval(date("m", strtotime($lastDate)));
        $result = array();
        for ($i = $start; $i <= $end; $i++) {
            $firstDay = date("Y-" . $i . "-01", strtotime($firstDate));
            $lastDay = date("Y-m-d", strtotime("$firstDay +1 month -1 day"));
            $result[] = array('firstday' => $i == $start ? $firstDate : $firstDay, 'lastday' => $i == $end ? $lastDate : $lastDay);
        }
        return $result;
    }
}

if (!function_exists('get_month_list')) {

    /**
     * 获取月份列表
     * @param $startTime
     * @param $endTime
     * @return array
     */
    function get_month_list($startTime, $endTime): array
    {
        $startMonth = date('Y-m', $startTime);  //开始日期 年-月
        $endMonth = date('Y-m', $endTime);    //结束日期 年-月
        $currentMonth = $startMonth;
        $returnTimes = [];
        do {
            $returnTimes[date('Y', strtotime($currentMonth))][] = date('m', strtotime($currentMonth));

            $currentMonth = date("Y-m", strtotime($currentMonth . ' + 1 month'));
        } while (strtotime($currentMonth) <= strtotime($endMonth));
        return $returnTimes;
    }
}

if (!function_exists('get_month_day_list')) {
    /**
     * 获取年月日列表
     * @param int $startTime
     * @param int $endTime
     * @return array
     */
    function get_month_day_list(int $startTime, int $endTime): array
    {
        if ($endTime < $startTime) {
            $startTime ^= $endTime ^= $startTime ^= $endTime;
        }
        $startMonthDay = strtotime(date('Y-m-d', $startTime));  //开始日期 年-月
        $endMonthDay = strtotime(date('Y-m-d', $endTime));    //结束日期 年-月
        $currentMonthDay = $startMonthDay;

        $returnTimes = [];
        while ($currentMonthDay <= $endMonthDay) {
            $returnTimes[] = date('Y-m-d', $currentMonthDay);
            $currentMonthDay = strtotime(' + 1 day', $currentMonthDay);
        }
        return $returnTimes;
    }
}

if (!function_exists('camelize')) {
    /**
     *  下划线转驼峰
     * 思路:
     * step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
     * step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
     * @param $unCamelizeWords
     * @param string $separator
     * @return string
     */
    function camelize($unCamelizeWords, string $separator = '_'): string
    {
        $unCamelizeWords = $separator . str_replace($separator, " ", strtolower($unCamelizeWords));
        return str_replace(" ", "", ucwords(ltrim($unCamelizeWords, $separator)));
    }
}

if (!function_exists('trim_time_range_end')) {
    /**
     * 处理查询时间范围，最晚查询时间不能超过今天 23:59:59
     * @param $timeRangeEnd
     * @return int
     */
    function trim_time_range_end($timeRangeEnd, $trimEndTime = 0): int
    {
        $todayEndTime = $trimEndTime ?? get_today_end_time();
        if ($timeRangeEnd > $todayEndTime) {
            $timeRangeEnd = $todayEndTime;
        }
        return (int)$timeRangeEnd;
    }
}

if (!function_exists('get_today_start_time')) {
    /**
     * 获取今天开始的时间戳
     * @return false|int
     */
    function get_today_start_time()
    {
        return strtotime(date('Y-m-d 00:00:00'), time());
    }
}

if (!function_exists('get_today_end_time')) {
    /**
     * 获取今天结束的时间戳
     * @return false|int
     */
    function get_today_end_time()
    {
        return strtotime(date('Y-m-d 23:59:59'), time());
    }
}
